package com.ygkj.demo

import android.util.Log
import java.io.*
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream

/**
 * Zip压缩/解压缩工具类
 * 实现对目标路径及其子路径下的所有文件及空目录的压缩
 *
 * Title: ZipUtil1
 *
 * Description:
 */
object ZipUtils {
    private const val TAG = "ZipUtils"

    /**
     * 缓冲器大小
     */
    private const val BUFFER = 1024

    /**
     * 取的给定源目录下的所有文件及空的子目录
     * 递归实现
     *
     * @param srcFile
     * @return
     */
    private fun getAllFiles(srcFile: File): List<File> {
        val fileList: MutableList<File> = ArrayList()
        val tmp = srcFile.listFiles()
        for (i in tmp.indices) {
            if (tmp[i].isFile) {
                fileList.add(tmp[i])
                println("add file: " + tmp[i].name)
            }
            if (tmp[i].isDirectory) {
                if (tmp[i].listFiles().size != 0) { //若不是空目录，则递归添加其下的目录和文件
                    fileList.addAll(getAllFiles(tmp[i]))
                } else { //若是空目录，则添加这个目录到fileList
                    fileList.add(tmp[i])
                    println("add empty dir: " + tmp[i].name)
                }
            }
        } // end for
        return fileList
    }

    /**
     * 取相对路径
     * 依据文件名和压缩源路径得到文件在压缩源路径下的相对路径
     *
     * @param dirPath 压缩源路径
     * @param file
     * @return 相对路径
     */
    private fun getRelativePath(dirPath: String, file: File): String {
        var file: File? = file
        val dir = File(dirPath)
        var relativePath = file!!.name
        while (true) {
            file = file!!.parentFile
            if (file == null) {
                break
            }
            relativePath = if (file == dir) {
                break
            } else {
                file.name + "/" + relativePath
            }
        } // end while
        return relativePath
    }

    /**
     * 创建文件
     * 根据压缩包内文件名和解压缩目的路径，创建解压缩目标文件，
     * 生成中间目录
     *
     * @param dstPath  解压缩目的路径
     * @param fileName 压缩包内文件名
     * @return 解压缩目标文件
     * @throws IOException
     */
    @Throws(IOException::class)
    fun createFile(dstPath: String, fileName: String): File {
        val dirs = fileName.split("/").toTypedArray() //将文件名的各级目录分解
        var file = File(dstPath)
        return if (dirs.size > 1) { //文件有上级目录
            for (i in 0 until dirs.size - 1) {
                file = File(file, dirs[i]) //依次创建文件对象知道文件的上一级目录
            }
            if (!file.exists()) {
                file.mkdirs() //文件对应目录若不存在，则创建
                println("mkdirs: " + file.canonicalPath)
            }
            file = File(file, dirs[dirs.size - 1]) //创建文件
            file
        } else {
            if (!file.exists()) {
                file.mkdirs() //若目标路径的目录不存在，则创建
                println("mkdirs: " + file.canonicalPath)
            }
            file = File(file, dirs[0]) //创建文件
            file
        }
    }

    /**
     * 解压缩方法
     *
     * @param zipFileName 压缩文件名
     * @param dstPath     解压目标路径
     * @return
     */
    fun unzip(zipFileName: String?, dstPath: String): Boolean {
        println("zip uncompressing...")
        Log.d(TAG, "开始解压 ................")
        try {
            val zipInputStream = ZipInputStream(FileInputStream(zipFileName))
            var zipEntry: ZipEntry? = null
            val buffer = ByteArray(BUFFER) //缓冲器
            var readLength = 0 //每次读出来的长度
            while (zipInputStream.nextEntry.also { zipEntry = it } != null) {

                if (zipEntry!!.isDirectory) { //若是zip条目目录，则需创建这个目录
                    val dir = File(dstPath + "/" + zipEntry!!.name)
                    if (!dir.exists()) {
                        dir.mkdirs()
                        println("mkdirs: " + dir.canonicalPath)
                        Log.d(TAG, "解压目录 = " + dir.canonicalPath)
                        continue  //跳出
                    }
                }
                var name = zipEntry!!.name
                var replace = name.replace("\\", "/")
                val file = createFile(dstPath, replace) //若是文件，则需创建该文件
                println("file created: " + file.canonicalPath)
                Log.d(TAG, "创建文件 = " + file.canonicalPath)
                val outputStream: OutputStream = FileOutputStream(file)
                while (zipInputStream.read(buffer, 0, BUFFER).also { readLength = it } != -1) {
                    outputStream.write(buffer, 0, readLength)
                }
                outputStream.close()
                println("file uncompressed: " + file.canonicalPath)
                Log.d(TAG, "*** = " + file.canonicalPath)
            } // end while
        } catch (e: FileNotFoundException) {
            println(e.message)
            e.printStackTrace()
            println("unzip fail!")
            return false
        } catch (e: IOException) {
            println(e.message)
            e.printStackTrace()
            println("unzip fail!")
            return false
        }
        println("unzip success!")
        return true
    }

    /**
     * 压缩方法
     * （可以压缩空的子目录）
     *
     * @param srcPath     压缩源路径
     * @param zipFileName 目标压缩文件
     * @return
     */
    fun zip(srcPath: String, zipFileName: String?): Boolean {
        println("zip compressing...")
        val srcFile = File(srcPath)
        val fileList = getAllFiles(srcFile) //所有要压缩的文件
        val buffer = ByteArray(BUFFER) //缓冲器
        var zipEntry: ZipEntry? = null
        var readLength = 0 //每次读出来的长度
        try {
            val zipOutputStream = ZipOutputStream(FileOutputStream(zipFileName))
            for (file in fileList) {
                if (file.isFile) { //若是文件，则压缩这个文件
                    zipEntry = ZipEntry(getRelativePath(srcPath, file))
                    zipEntry.size = file.length()
                    zipEntry.time = file.lastModified()
                    zipOutputStream.putNextEntry(zipEntry)
                    val inputStream: InputStream = BufferedInputStream(FileInputStream(file))
                    while (inputStream.read(buffer, 0, BUFFER).also { readLength = it } != -1) {
                        zipOutputStream.write(buffer, 0, readLength)
                    }
                    inputStream.close()
                    println("file compressed: " + file.canonicalPath)
                } else { //若是目录（即空目录）则将这个目录写入zip条目
                    zipEntry = ZipEntry(getRelativePath(srcPath, file) + "/")
                    zipOutputStream.putNextEntry(zipEntry)
                    println("dir compressed: " + file.canonicalPath + "/")
                }
            } // end for
            zipOutputStream.close()
        } catch (e: FileNotFoundException) {
            println(e.message)
            e.printStackTrace()
            println("zip fail!")
            return false
        } catch (e: IOException) {
            println(e.message)
            e.printStackTrace()
            println("zip fail!")
            return false
        }
        println("zip success!")
        return true
    }

    /**
     * 删除文件目录和文件
     * @param dir String
     * @param isDelDir Boolean 是否当前删除目录默认删除
     * @return Boolean
     */
    fun deleteDirectory(dir: String, isDelDir: Boolean = true): Boolean {
        // 如果dir不以文件分隔符结尾，自动添加文件分隔符
        var dir = dir
        if (!dir.endsWith(File.separator)) dir = dir + File.separator
        val dirFile = File(dir)
        // 如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory) {
            println("删除目录失败：" + dir + "不存在！")
            return false
        }
        var flag = true
        // 删除文件夹中的所有文件包括子目录
        val files = dirFile.listFiles()
        for (i in files.indices) {
            // 删除子文件
            if (files[i].isFile) {
                flag = deleteFile(files[i].absolutePath)
                if (!flag) break
            } else if (files[i].isDirectory) {
                flag = deleteDirectory(
                    files[i]
                        .absolutePath
                )
                if (!flag) break
            }
        }
        if (!flag) {
            println("删除目录失败！")
            return false
        }
        // 删除当前目录
        if (isDelDir) {
            return if (dirFile.delete()) {
                println("删除目录" + dir + "成功！")
                true
            } else {
                false
            }
        }

       return true
    }

    /**
     * 删除单个文件
     * @param fileName String
     * @return Boolean
     */
    fun deleteFile(fileName: String): Boolean {
        val file = File(fileName)
        // 如果文件路径所对应的文件存在，并且是一个文件，则直接删除
        return if (file.exists() && file.isFile) {
            if (file.delete()) {
                println("删除单个文件" + fileName + "成功！")
                true
            } else {
                println("删除单个文件" + fileName + "失败！")
                false
            }
        } else {
            println("删除单个文件失败：" + fileName + "不存在！")
            false
        }
    }
}